GLSL Shader 范例集 [ 001 - 020 ]
范例集是个人在学习 GLSL 编程时的练习梳理,部分范例参考自《 The Book Of Shaders 》,其余部分根据个人理解,加入延展扩充
什么是 Shader?
阅读指南
若此前未接触过 shader 编程,推荐结合《 The Book Of Shaders 》一书( 作者是 Patricio Gonzalez Vivo 与 Jen Lowe。前 11 章有中文翻译,译者为 Tornote ,Artrustee 。
下文的范例使用框架为 Openframeworks。改写到 Processing 上运行可参考
()范例从最基础的概念开始,以应用为导向,由浅入深。通过目录可快速回顾相关知识点
准备-Xcode 中的简便操作技巧
下面分享一个小技巧,可以在 Xcode 中快速编辑 shader
准备工作,先在 data 中创建文件 shader.frag 并保存
创建文件后,可在左侧创建一个名为 shader 的文件夹。方法是在项目名处右键,点 new Group。之后将 shader.frag 拖动到其中,接着勾选 copy items if needed ,并点 finish。
现在就可以在 xcode 中仅仅通过点击来切换文件,方便随时修改 shader
Shader 范例集目录
001-对屏幕平涂指定颜色
002-确定一个分界线,对屏幕指定的一边平涂颜色
003-绘制宽度为 1 像素的垂直直线
004-绘制宽度为指定像素的垂直直线
005-绘制矩形
006-绘制圆形
004-绘制垂直直线
005-传入时间变量,自动变色
006-传入鼠标位置,在shader中移动圆
007-传入外部变量,随时间变色
008-传入鼠标坐标,在shader中移动圆
009-绘制过原点的直线, mouseX 控制斜率
010-绘制竖条纹,mouseX 控制条纹宽度
011-绘制黑白格子,mouseX 控制格子宽度
012-绘制 sin 函数曲线,鼠标控制曲线位移
013-混合指定两种颜色
014-绘制带软阴影的圆
015-绘制带软阴影的矩形
016-绘制 smoothstep 函数的变化曲线
017-绘制只带描边的圆形
018-绘制只带描边的矩形
019-变换坐标系以适配屏幕
020-绘制距离场,对比 length 与 dot
001-对屏幕平涂指定颜色
知识点:理解如何在 OF 中加载 shader,理解 gl_FragColor ,vec4 的概念。gl_FragColor 可以理解为当前像素的色值,vec4 可用于表示色彩,分别对应 R,G,B,A , 最大值为 1
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
建议 vec4() 中的数值写成小数,虽然某些显卡上不会报错,但写成小数会更规范
shader 的效果可以理解成一个贴图,在上面例子中,通过 begin 和 end,它就会将效果投射到与画布等大的矩形中。可以试着修改矩形的比例或是换成其他绘图函数对照效果
002-确定一个分界线,对屏幕指定的一边平涂颜色
知识点:理解 gl_FragCoord
方法一:
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
if(gl_FragCoord.x < 200.0){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}else{
gl_FragColor = vec4(vec3(0.0),1.0);
}
}
方法二:
知识点:理解 step
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
float val = step(gl_FragCoord.x,200.0);
gl_FragColor = vec4(val * vec3(1.0,0.0,0.0),1.0);
}
shader 编程中,多用 step 函数来替换 if。此方法更简洁,规范。
step(float a,float b) 是一个插值函数,它需要输入两个参数。第一个是阀值,第二个是传入的判定数值。当 a <= b,则返回 1.0。若 a > b,则返回 0.0。
float val = step(gl_FragCoord.x,200.0) 等价于 float val = 1 -step(200.0,gl_FragCoord.x);
003-绘制宽度为 1 像素的垂直直线
方法一:绘制宽度为 1 像素的直线
知识点:更准确地理解 gl_FragCoord 的分量 x,y。其中的小数部分恒为0.5
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
if(gl_FragCoord.x == 200.5){
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
小数部分若不写 .5 , 直线不会被显示出来
绘制横向的直线则写成 gl_FragCoord.y
方法二:绘制宽度为 1 像素的直线
知识点:
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
float val = step(200.5,gl_FragCoord.x) * step(gl_FragCoord.x,200.5);
gl_FragColor = vec4(val * vec3(1.0),1.0);
}
并不是绘制直线的最优写法,但有助于透彻理解 step 函数。
乘号相当于 &&
004-绘制宽度为指定像素的垂直直线
知识点:理解 step,上例的延伸版
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
void main(void){
float w = 50.0; // 直线宽度
float startX = 300.5; // 起始位置
vec3 color = vec3(step(startX - w/2,gl_FragCoord.x) * step(gl_FragCoord.x,startX + w/2));
gl_FragColor = vec4(color,1.0);
}
005-绘制矩形
方法一:
知识点:理解 gl_FragCoord
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
if(gl_FragCoord.x > 200.0 && gl_FragCoord.x < 600.0 && gl_FragCoord.y > 200.0 && gl_FragCoord.y < 400.0){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
理解绘制矩形的判定条件
方法二:
知识点:深入理解 step,可传入 vec2 类型
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
vec2 left = step(vec2(200,200),gl_FragCoord.xy);
vec2 right = step(gl_FragCoord.xy,vec2(600,400));
float val = left.x * left.y * right.x * right.y;
gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0);
}
step 函数中传入 vec2 类型可以这样理解。
vec2 left = step(vec2(200,200),gl_FragCoord.xy)
等价于
vec2 left; left.x = step(200,gl_FragCoord.x); left.y = step(200,gl_FragCoord.y);
006-绘制圆形
方法一:
知识点:理解 distance ,vec2 的用法
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
vec2 center = vec2(512,350);
if(distance(center,gl_FragCoord.xy) < 200){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
vec2 可表示二维点坐标
distance 函数必须传入两个 vec2 类型,gl_FragCoord 是 vec4 类型,因此不能直接使用,需要写作 gl_FragCoord.xy。它等价于 vec2(gl_FragCoord.x,gl_FragCoord.y)
补充知识:gl_FragCoord 的四个分量分别对应x, y, z和1/w。其中,x和y是当前片元的窗口相对坐标,但不是整数,小数部分恒为0.5。详细说明可参考(
方法二:
知识点:理解 step
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
void main(void){
vec2 center = vec2(512,350);
vec3 color = vec3(step(200,distance(center,gl_FragCoord.xy)));
gl_FragColor = vec4(color,1.0);
}
007-传入外部变量,随时间变色
知识点:理解 setUniform1f ,从主程序传入数据
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
ofBackground(0);
shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
ofDrawCircle(mouseX,mouseY,200);
shader.end();
}
— Fragment Shader 内
#version 120
uniform float u_time; // 记得加上分号
void main( void ){
float r = abs(sin(u_time));
float g = abs(cos(u_time));
float b = 0.5;
gl_FragColor = vec4(r,g,b,1.0);
}
注意,函数 setUniform1f 必须放在 begin 和 end 之内
008-传入鼠标坐标,在shader中移动圆
知识点:理解 setUniform2f
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float val = step(distance(u_mouse,gl_FragCoord.xy),100);
gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0);
}
注意,因为 GPU 中的坐标与 OF 中的纵坐标朝向是相反的,所以需要在传入时,需写成 ofGetHeight() - mouseY
009-绘制过原点的直线, mouseX 控制斜率
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float k = u_mouse.x/100.0;
float y = gl_FragCoord.x * k; // 直线方程
float val = step(gl_FragCoord.y,y) * step(y,gl_FragCoord.y + 20.0);
gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0);
}
010-绘制竖条纹,mouseX 控制条纹宽度
知识点:理解 mod
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float interval = u_mouse.x / 10.0;
if(mod(int(gl_FragCoord.x / interval),2) == 0){
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
mod(x,y) 为取模运算,等价于 x % y
011-绘制黑白格子,mouseX 控制格子宽度
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float interval = u_mouse.x / 10.0;
if(mod(int(gl_FragCoord.x / interval),2) == 0){
if(mod(int(gl_FragCoord.y / interval),2) == 0){
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}else{
if(mod(int(gl_FragCoord.y / interval),2) == 0){
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}else{
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}
}
}
012-绘制 sin 函数曲线,鼠标控制曲线位移
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float y = 300 + sin((gl_FragCoord.x + u_mouse.x)/100.0) * 200;
float val = step(gl_FragCoord.y,y + u_mouse.y) * step(y + u_mouse.y,gl_FragCoord.y + 20);
gl_FragColor = vec4(vec3(val),1.0);
}
013-混合指定两种颜色
知识点:理解 mix 函数,作用是混合色彩
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
uniform vec2 u_resolution;
void main(void){
vec4 colorA,colorB;
colorA = vec4(1.0,0.0,0.0,1.0);
colorB = vec4(0.0,1.0,0.0,1.0);
gl_FragColor = mix(colorA,colorB,u_mouse.x/u_resolution.x);
}
014-绘制带软阴影的圆
知识点:理解 length ,smoothstep
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
uniform vec2 u_resolution;
void main(void){
vec2 center = u_resolution.xy/2;
float shadowW = u_mouse.x/5.0;
float dist = length(gl_FragCoord.xy - center);
if(dist < 100){
gl_FragColor = vec4(0,0,0,1);
}else if(dist < 100 + shadowW){
vec4 colorA = vec4(vec3(0.7),1.0);
vec4 colorB = vec4(vec3(1.0),1.0);
// 使用 smoothstep
gl_FragColor = mix(colorA,colorB,smoothstep(100,100 + shadowW,dist));
// 不使用 smoothstep
//gl_FragColor = mix(colorA,colorB,(dist - 100.0)/shadowW);
}else{
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}
}
length(a - b) 等价于 distance(a,b),它能计算某向量的长度
smoothstep(float a,float b,float x)函数,它的返回值固定为 0 到 1 之间的过渡值。a,b 表示 x 范围的上下限,x 代表传入的参数。输出值的变化是非线性的,会作平滑处理
smoothstep 适当使用,可起抗锯齿效果
015-绘制带软阴影的矩形
知识点:理解 length ,smoothstep
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
vec2 ptA = vec2(300,300);
vec2 ptB = vec2(700,400);
if(step(ptA,gl_FragCoord.xy) * step(gl_FragCoord.xy,ptB) == vec2(1.0)){
gl_FragColor = vec4(vec3(0.0),1.0);
}else{
float shadowW = u_mouse.x/10.0;
vec2 left = smoothstep(ptA - vec2(shadowW),ptA ,gl_FragCoord.xy);
vec2 right = smoothstep(ptB + vec2(shadowW),ptB,gl_FragCoord.xy);
vec3 color = 1 - vec3(left.x * left.y * right.x * right.y) * 0.5;
gl_FragColor = vec4(color,1.0);
}
}
016-绘制 smoothstep 函数的变化曲线
知识点:深入理解 smoothstep,理解 abs
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
uniform vec2 u_resolution;
void main(void){
float h = 300;
if(gl_FragCoord.y < h){
float ratio = gl_FragCoord.x / u_resolution.x;
float val = step(abs(gl_FragCoord.y/h - ratio),0.01);
gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val + vec3(ratio) * (1 - val),1.0);
}else if(gl_FragCoord.y > h + 100){
float ratio = smoothstep(0,u_resolution.x,gl_FragCoord.x);
float val = step(abs((gl_FragCoord.y - 400.0)/h - ratio),0.01);
gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val + vec3(ratio) * (1 - val),1.0);
}else{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
017-绘制只带描边的圆形
知识点:理解 step
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
float drawCircle(vec2 pos,float r,float lineWidth){
float r1 = r - lineWidth/2;
float r2 = r + lineWidth/2;
return step(r1,distance(gl_FragCoord.xy,pos)) * step(distance(gl_FragCoord.xy,pos),r2);
}
void main(void){
vec2 center = vec2(512,350);
gl_FragColor = vec4(vec3(drawCircle(center,200,20)),1.);
}
自定义函数必须写在 main 前
018-绘制只带描边的矩形
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
float drawRect(vec2 pos,vec2 size,float lineWidth){
// 外部矩形
vec2 rect1_pos1 = pos - size/2 - vec2(lineWidth/2);
vec2 rect1_pos2 = pos + size/2 + vec2(lineWidth/2);
vec2 rect1_bl = step(rect1_pos1,gl_FragCoord.xy);
vec2 rect1_tr = step(gl_FragCoord.xy,rect1_pos2);
// 内部矩形
vec2 rect2_pos1 = pos - size/2 + vec2(lineWidth/2);
vec2 rect2_pos2 = pos + size/2 - vec2(lineWidth/2);
vec2 rect2_bl = step(rect2_pos1,gl_FragCoord.xy);
vec2 rect2_tr = step(gl_FragCoord.xy,rect2_pos2);
return rect1_bl.x * rect1_bl.y * rect1_tr.x * rect1_tr.y * (1.0 - rect2_bl.x * rect2_bl.y * rect2_tr.x * rect2_tr.y);
}
void main(void){
vec2 center = vec2(512,350);
gl_FragColor = vec4(vec3(drawRect(center,vec2(600,400),20)),1.);
}
019-变换坐标系以适配屏幕
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_resolution", ofGetWidth(),ofGetHeight());
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_resolution;
float drawCircle(vec2 curPos,vec2 pos,float r,float lineWidth){
float r1 = r - lineWidth/2;
float r2 = r + lineWidth/2;
return step(r1,distance(curPos,pos)) * step(distance(curPos,pos),r2);
}
void main(void){
vec2 st = gl_FragCoord.xy/u_resolution;
st -= vec2(0.5);
st *= 2.0;
// 对比开启与关闭的区别
st.x *= u_resolution.x/u_resolution.y;
vec2 center = vec2(0.0);
gl_FragColor = vec4(vec3(drawCircle(st,center,0.5,0.1)),1.);
}
思路:将坐标的变化范围进行映射,例如以 y 轴作为基准。原本 y 的坐标变化是从 0 到屏幕宽,现在映射为 -1 到 1。为了让图形不产生拉伸,x 轴的变化范围则需要根据屏幕的宽高比进行调整。经过系列操作,坐标原点(0,0)会处于屏幕中央
优点1:此方法可以确保在不同分辨率的屏幕上,比例可以自适应
优点2:绘制对称图形,坐标表达会更方便
020-绘制距离场,对比 length 与 dot
方法一:使用 length
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
float l = length(gl_FragCoord.xy - vec2(512,350));
gl_FragColor = vec4(vec3(fract(u_mouse.x/10000.0 * l)),1.);
}
函数 fract(float x) 的作用是去掉整数部分,只取小数部分。因此产生黑白相间的循环效果
方法二:使用 dot
— ofApp.h 内
ofShader shader;
— ofApp.cpp 内
void ofApp::update(){
shader.load("shader");
}
void ofApp::draw(){
shader.begin();
shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);
ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());
shader.end();
}
— Fragment Shader 内
#version 120
uniform vec2 u_mouse;
void main(void){
vec2 temp = gl_FragCoord.xy - vec2(512,350);
gl_FragColor = vec4(vec3(fract(u_mouse.x/1000000.0 * dot(temp,temp))),1.);
}
使用 dot 函数,图形的计算效率会更高。但使用它绘制得到的距离场是非线性变化的
END
学习 Shader 的过程中有一个体会。在 Processing 或 Openframeworks 中使用已有的绘图函数,就像是用现成的基本原料来搭建东西。这些原料是木材,是石头。需要你一砖一瓦地构建才能做出高楼。而用 shader 编程,就像是拥有一台性能极强的 3D 打印机,可以允许在分子原子层级去搭建物体。这种神奇的力量是由 GPU 的强大性能所赋予的。我们要做的只是提前编写好逻辑,考虑好局部像素与全局图形之间的关系,它便能帮你瞬间造物(图像)。